package db

import (
	"bytes"
	"encoding/binary"
	"testing"

	"hundsun.com/hsl/hschain/common/util/random"

	"github.com/stretchr/testify/assert"
)

func int642Bytes(i int64) []byte {
	buf := make([]byte, 8)
	binary.BigEndian.PutUint64(buf, uint64(i))
	return buf
}

func bytes2Int64(buf []byte) int64 {
	return int64(binary.BigEndian.Uint64(buf))
}

// BaseTestDBGets 测试DB的Gets接口
func BaseTestDBGets(t *testing.T, dbm Database) {
	keys := [][]byte{
		[]byte("hello1"),
		[]byte("hello2"),
		[]byte("hello3"),
		[]byte("hello4"),
		[]byte("hello5"),
	}
	batch := dbm.NewBatch(true)
	for _, k := range keys {
		batch.Set(k, k)
	}
	batch.Write()

	var testkey [][]byte
	testkey = append(testkey, keys[:2]...)
	testkey = append(testkey, []byte("extr1"))
	testkey = append(testkey, keys[2:]...)
	testkey = append(testkey, []byte("extr2"))
	values, err := dbm.Gets(testkey)
	assert.NoError(t, err)
	assert.Equal(t, len(testkey), len(values))
	for i, v := range values[:len(testkey)-1] {
		if i == 2 {
			assert.Nil(t, v)
		} else {
			assert.NotNil(t, v)
		}
	}
	assert.Nil(t, values[len(testkey)-1])
}

// BaseBenchBatchWrites 测试批量写
func BaseBenchBatchWrites(b *testing.B, size int, dbm Database) {
	batch := dbm.NewBatch(true)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		key := random.GetRandBytes(20, 64)
		value := random.GetRandBytes(size*1024, size*1024)
		b.StartTimer()
		batch.Set(key, value)
		if i > 0 && i%100 == 0 {
			err := batch.Write()
			assert.Nil(b, err)
			batch = dbm.NewBatch(true)
		}
	}
	err := batch.Write()
	assert.Nil(b, err)
	b.StopTimer()
}

// BaseBenchRandomReadsWrites 测试随机读写
func BaseBenchRandomReadsWrites(b *testing.B, dbm Database) {
	numItems := int64(1000000)
	internal := map[int64]int64{}
	for i := 0; i < int(numItems); i++ {
		internal[int64(i)] = int64(0)
	}

	for i := 0; i < b.N; i++ {
		// Write something
		{
			idx := (int64(random.RandInt()) % numItems)
			internal[idx]++
			val := internal[idx]
			idxBytes := int642Bytes(idx)
			valBytes := int642Bytes(val)
			//fmt.Printf("Set %X -> %X\n", idxBytes, valBytes)
			dbm.Set(
				idxBytes,
				valBytes,
			)
		}
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		// Read something
		{
			idx := (int64(random.RandInt()) % numItems)
			val := internal[idx]
			idxBytes := int642Bytes(idx)
			valBytes, _ := dbm.Get(idxBytes)
			//fmt.Printf("Get %X -> %X\n", idxBytes, valBytes)
			if val == 0 {
				if !bytes.Equal(valBytes, nil) {
					b.Errorf("Expected %v for %v, got %X",
						nil, idx, valBytes)
					break
				}
			} else {
				if len(valBytes) != 8 {
					b.Errorf("Expected length 8 for %v, got %X",
						idx, valBytes)
					break
				}
				valGot := bytes2Int64(valBytes)
				if val != valGot {
					b.Errorf("Expected %v for %v, got %v",
						val, idx, valGot)
					break
				}
			}
		}
	}
	b.StopTimer()
}

// BaseTestDBBatch 测试DB的batch接口功能
func BaseTestDBBatch(t *testing.T, dbm Database) {
	batch := dbm.NewBatch(false)
	batch.Set([]byte("hello"), []byte("world"))
	err := batch.Write()
	assert.Nil(t, err)

	batch = dbm.NewBatch(false)
	v, err := dbm.Get([]byte("hello"))
	assert.Nil(t, err)
	assert.Equal(t, v, []byte("world"))

	//set and del
	batch.Set([]byte("hello1"), []byte("world"))
	batch.Set([]byte("hello2"), []byte("world"))
	batch.Set([]byte("hello3"), []byte("world"))
	batch.Set([]byte("hello4"), []byte("world"))
	batch.Set([]byte("hello5"), []byte("world"))
	batch.Delete([]byte("hello1"))
	err = batch.Write()
	assert.Nil(t, err)
	v, err = dbm.Get([]byte("hello1"))
	assert.Nil(t, v)
}
